/**
\file LoSetup.h
*/
#pragma once
#include <lostlbase.h>
#include <stdexcept>
#include "System.h"
#include <LoSetupapi.h>

namespace lo {

	/**
	Setup base class for installing Windows components.
	*/
	class Setup
	{
	public:
		class InfException : public std::exception
		{
		public:
			InfException(const char *file, unsigned errorLine);
		};

		class InfLine : public object_base
		{
			friend class InfFile;
		public:
			InfLine();
			InfLine(PINFCONTEXT context) : m_context(*context) {}
			/** Returns one of the substrings in an inf line (separated by commas). When \p fieldIndex 0 = the key, 1 = first string value, 2 = second, etc... */
			bool	getStringField( __in DWORD fieldIndex, __out string & field );
			/** Moves the line context to the next line within the current section. */
			bool	moveToNextLine();
			bool	isValidLine();
			INFCONTEXT	m_context;
		};

		union FileNotify {
		public:
			FileNotify( int init ) : _value(init) {}
			enum {
				COPYERROR = 0x0d,		/// An error occurred during a file copy operation.
				DELETEERROR = 0x07,		/// An error occurred during a file deletion operation.
				ENDCOPY = 0x0c,			/// A file copy operation has ended.
				ENDDELETE = 0x06,		/// A file deletion operation has ended.
				ENDQUEUE = 0x02,		/// The queue has finished committing.
				ENDREGISTRATION = 0x20,	/// The registration or unregistration of the file has finished.
				ENDRENAME = 0x09,		/// A file rename operation has ended.
				ENDSUBQUEUE = 0x04,		/// A subqueue (either copy, rename or delete) has ended.
				FILEEXTRACTED = 0x13,	/// The file has been extracted from the cabinet.
				FILEINCABINET = 0x11,	/// A file is encountered in the cabinet.
				FILEOPDELAYED = 0x14,	/// The file was in use, and the current operation has been delayed until the system is rebooted.
				LANGMISMATCH = 0x10000,	/// The language of the current operation does not match the system language.
				NEEDMEDIA = 0x0e,		/// New source media is required.
				NEEDNEWCABINET = 0x12,	/// The current file is continued in the next cabinet.
				QUEUESCAN = 0x0f,		/// A node in the file queue has been scanned.
				QUEUESCAN_EX = 0x18,	/// A node in the file queue has been scanned. Extended notification for SetupScanFileQueue(Flags=SPQ_SCAN_USE_CALLBACKEX)
				QUEUESCAN_SIGNERINFO = 0x40,	/// A node in the file queue has been scanned. Extended notification for SetupScanFileQueue(Flags=SPQ_SCAN_USE_CALLBACK_SIGNERINFO)
				RENAMEERROR = 0x0a,		/// An error occurred during a file rename operation.
				STARTCOPY = 0x0b,		/// A file copy operation has started.
				STARTDELETE = 0x05,		/// A file delete operation has started.
				STARTQUEUE = 0x01,		/// The queue has started to commit.
				STARTREGISTRATION = 0x19,/// The registration or unregistration of the file has started.
				STARTRENAME = 0x08,		/// A file rename operation has started.
				STARTSUBQUEUE = 0x03,	/// A subqueue (either copy, rename or delete) has started.
				TARGETEXISTS = 0x20000,	/// A copy of the specified file already exists on the target.
				TARGETNEWER = 0x40000	/// A newer version of the specified file exists on the target.
			};
			inline operator int() { return _value; }
			inline bool HasFlag( int check) { return (check & _value) != 0; }
		private:
			int _value;
		};
		typedef bool (*ProgressCallback)(FileNotify fileNotify, const char *source, const char *destination);

		/**
		You can only open an InfFile through setupapi.
		*/
		class InfFile : public object_base
		{
			friend class InfLine;
		public:
			~InfFile();
			InfLine			findFirstLine(const char *section, const char *key) const;
			bool			getLineText(const char *section, const char *key, string &text) const;
			bool			getLineBool(const char *section, const char *key, bool flags) const;
			/** Returns the full path to the source file given the name of the \p sourceFile */
			string			getSourceFileLocation(const char *sourceFile);
			bool			queryInfVersionInformation(const char *key, string &text, unsigned index = 0) const;
			static lo::ref<InfFile>	openInfFile(const char *filePath);
			void			setDirectoryId(unsigned id, const char *directory) const;
			bool			installFile(const char *sourceFileName, const char *section);
			void			installFromInfSection( const char * sectionName, const char * sourceRootPath, UINT flags = 0x1F, UINT copyFlags = 0x4, HKEY RelativeKeyRoot = NULL, HWND Owner = NULL, Setup::ProgressCallback MsgHandler =nullptr );
			/** Installs any CopyFiles commands found within the given section. true if a copyfiles section was found and false if not. throws exception on error. */
			bool			installCopyFiles( const char *section );
			/** Installs the entire section, assuming that the section is a CopyFiles sections */
			bool			installCopyFilesSections( const char *section );
			enum {	LOGCONFIG = 1,	/*!< This flag is only used if DeviceInfoSet and DeviceInfoData are specified. */
					INIFILES = 2,	/*!< Perform INI-file operations (UpdateInis, UpdateIniFields lines in the Install section being processed). */
					REGISTRY = 4,	/*!< Perform registry operations (AddReg, DelReg lines in the Install section being processed). */
					INI2REG = 8,	/*!< Perform INI-file to registry operations (Ini2Reg lines in the Install section being processed). */
					FILES = 0x10,	/*!< Perform file operations (CopyFiles, DelFiles, RenFiles lines in the Install section being processed). */
					BITREG = 0x20,	/*!<  */
					REGSVR = 0x40,	/*!< To send a notification to the callback routine when registering a file, include SPINST_REGISTERCALLBACKAWARE plus SPINST_REGSVR in Flags. The caller must also specify the MsgHandler parameter. */
					UNREGSVR = 0x80,/*!< To send a notification to the callback routine when unregistering a file, include SPINST_REGISTERCALLBACKAWARE plus SPINST_UNREGSVR in the Flags. The caller must also specify the MsgHandler parameter. */
					PROFILEITEMS = 0x100,
					COPYINF = 0x200,
					SINGLESECTION = 0x10000,
					LOGCONFIG_IS_FORCED = 0x00020000, /*!<  */
					LOGCONFIGS_ARE_OVERRIDES = 0x00040000, /*!<  */
					REGISTERCALLBACKAWARE = 0x00080000 /*!< */
					};
			enum {
					COPY_DELETESOURCE        = 0x0000001,   /*!< delete source file on successful copy */
					COPY_REPLACEONLY         = 0x0000002,   /*!< copy only if target file already present */
					COPY_NEWER               = 0x0000004,   /*!< copy only if source newer than or same as target */
					COPY_NEWER_OR_SAME       = COPY_NEWER,
					COPY_NOOVERWRITE         = 0x0000008,   /*!< copy only if target doesn't exist */
					COPY_NODECOMP            = 0x0000010,   /*!< don't decompress source file while copying */
					COPY_LANGUAGEAWARE       = 0x0000020,   /*!< don't overwrite file of different language */
					COPY_SOURCE_ABSOLUTE     = 0x0000040,   /*!< SourceFile is a full source path */
					COPY_SOURCEPATH_ABSOLUTE = 0x0000080,   /*!< SourcePathRoot is the full path */
					COPY_IN_USE_NEEDS_REBOOT = 0x0000100,   /*!< System needs reboot if file in use */
					COPY_FORCE_IN_USE        = 0x0000200,   /*!< Force target-in-use behavior */
					COPY_NOSKIP              = 0x0000400,   /*!< Skip is disallowed for this file or section */
					FLAG_CABINETCONTINUATION = 0x0000800,   /*!< Used with need media notification */
					COPY_FORCE_NOOVERWRITE   = 0x0001000,   /*!< like NOOVERWRITE but no callback notification */
					COPY_FORCE_NEWER         = 0x0002000,   /*!< like NEWER but no callback notification */
					COPY_WARNIFSKIP          = 0x0004000,   /*!< system critical file: warn if user tries to skip */
					COPY_NOBROWSE            = 0x0008000,   /*!< Browsing is disallowed for this file or section */
					COPY_NEWER_ONLY          = 0x0010000,   /*!< copy only if source file newer than target */
					COPY_RESERVED            = 0x0020000,   /*!< was: SP_COPY_SOURCE_SIS_MASTER (deprecated) */
					COPY_OEMINF_CATALOG_ONLY = 0x0040000,   /*!< (SetupCopyOEMInf only) don't copy INF--just catalog */
					COPY_REPLACE_BOOT_FILE   = 0x0080000,   /*!< file must be present upon reboot (i.e., it's needed by the loader); this flag implies a reboot */
					COPY_NOPRUNE             = 0x0100000    /*!< never prune this file */
			};

		private:
			InfFile()	{}
			InfFile(void *handle,const char *fileName);
			void		*m_handle;
			string		m_fileName;
			string		m_directory;
			static UINT CALLBACK Local_PSP_FILE_CALLBACK( PVOID Context, UINT Notification, UINT_PTR Param1, UINT_PTR Param2 );
			ProgressCallback m_MsgHandler;
			Setupapi	m_setupapi;
			auto_ptr<SP_INF_INFORMATION>	m_infInformation;
		};

	private:
	};


}